﻿namespace Baddies.Events
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using Baddies.Utils;    

    /// <summary>
    /// This class handles the events from system to system.
    /// <remarks>
    /// Registering a new event: Add a new event to the EventManager with a specific name.
    /// Registering TO an event: Use the Register function.
    /// Unregistering FROM an event: Use the Unregister function.
    /// Trigger an event: Call the Trigger function with the appropiate params.
    /// Final note: Working with strings might be a bit slow, must consider it after.
    /// </remarks>
    /// </summary>
    public class EventManager
    {
        /// <summary>
        /// Singleton instance of the EventManager.
        /// </summary>
        private static EventManager instance = new EventManager();

        /// <summary>
        /// Keeps a dictionary of all the events the EventManager has,
        /// and references them by names on a string.
        /// </summary>
        private Dictionary<string, EventHandler> events;

        /// <summary>
        /// Gets the instance object, thus returning a reference to the singleton.
        /// </summary>
        /// <value>Reference to the EventManager.</value>
        public static EventManager GetEV
        {
            get { return instance; }
        }

        /// <summary>
        /// Initializes a new instance of the EventManager class.
        /// </summary>
        public EventManager()
        {
            this.events = new Dictionary<string, EventHandler>();
        }

        /// <summary>
        /// Registers a new event.
        /// Asserts the event name does not exist already.
        /// </summary>
        /// <param name="eventName">Name of the new event.</param>
        /// <param name="log">Whether to log the messages to that event.</param>
        public void RegisterNewEvent(string eventName, bool log)
        {
            Debug.Assert(
                !this.events.ContainsKey(eventName), 
                "Cannot register. There is already a " + eventName + " event.");

            this.events.Add(eventName, new EventHandler(this.DummyDelegate));
            if (log)
            {
                this.events[eventName] += this.PeekEvent;
            }
        }

        /// <summary>
        /// Removes an event from the EventManager.
        /// Asserts the event exists in the EventManager first.
        /// </summary>
        /// <param name="eventName">Name of the event to remove.</param>
        public void UnregisterEvent(string eventName)
        {
            Debug.Assert(
                this.events.ContainsKey(eventName),
                "Cannot unregister. There isn't a " + eventName + " event.");

            EventHandler eventToRemove = this.events[eventName];

            this.events.Remove(eventName);
        }

        /// <summary>
        /// Invokes the specified event.
        /// Asserts the event exists in the EventManager first.
        /// </summary>
        /// <param name="eventName">Name of the event.</param>
        /// <param name="sender">Object that invokes the event.</param>
        /// <param name="args">Arguments of the event call.</param>
        public void Trigger(string eventName, object sender, EventArgs args)
        {
            Debug.Assert(
                this.events.ContainsKey(eventName),
                "Cannot trigger. There isn't a " + eventName + " event.");

            this.events[eventName](sender, args);
        }

        /// <summary>
        /// Invokes the specified event.
        /// Asserts the event exists in the EventManager first.
        /// </summary>
        /// <param name="eventName">Name of the event.</param>
        /// <param name="sender">Object that invokes the event.</param>
        public void Trigger(string eventName, object sender)
        {
            this.Trigger(eventName, sender, null);
        }

        /// <summary>
        /// Invokes the specified event.
        /// Asserts the event exists in the EventManager first.
        /// </summary>
        /// <param name="eventName">Name of the event.</param>
        public void Trigger(string eventName)
        {
            this.Trigger(eventName, null, null);
        }

        /// <summary>
        /// Gets an event reference.
        /// Asserts the event exists in the EventManager first.
        /// </summary>
        /// <param name="eventName">Name of the event.</param>
        /// <returns>Reference to the specified event.</returns>
        public EventHandler Get(string eventName)
        {
            Debug.Assert(
                this.events.ContainsKey(eventName),
                "Cannot find. There isn't a " + eventName + " event.");

            return this.events[eventName];
        }

        /// <summary>
        /// Registers a handle to an existing event.
        /// </summary>
        /// <param name="eventName">Event name.</param>
        /// <param name="handle">Handle to register.</param>
        public void Register(string eventName, EventHandler handle)
        {
            Debug.Assert(
                this.events.ContainsKey(eventName),
                "Cannot find. There isn't a " + eventName + " event.");

            this.events[eventName] += handle;
        }

        /// <summary>
        /// Unregisters a handle to an existing event.
        /// </summary>
        /// <param name="eventName">Event name.</param>
        /// <param name="handle">Handle to register.</param>
        public void Unregister(string eventName, EventHandler handle)
        {
            Debug.Assert(
                this.events.ContainsKey(eventName),
                "Cannot find. There isn't a " + eventName + " event.");

            this.events[eventName] -= handle;
        }

        /// <summary>
        /// Clears all events from the event manager.
        /// </summary>
        public void Clear()
        {
            this.events.Clear();
        }

        /// <summary>
        /// Gets whether the event of eventName name exists in the EventManager.
        /// </summary>
        /// <param name="eventName">Name of the event.</param>
        /// <returns>True if it exists, false otherwise.</returns>
        public bool Exists(string eventName)
        {
            return this.events.ContainsKey(eventName);
        }

        /// <summary>
        /// Logs the content of a message.
        /// </summary>
        /// <param name="sender">Class that sends the event.</param>
        /// <param name="e">Arguments of the event.</param>
        private void PeekEvent(object sender, EventArgs e)
        {
            Log.F.WriteLine("Event sent! Args: " + e);
        }

        /// <summary>
        /// Dummy delegate for initializing registered messages.
        /// </summary>
        /// <param name="sender">Class that sends the event.</param>
        /// <param name="e">Arguments of the event.</param>
        private void DummyDelegate(object sender, EventArgs e)
        {
        }
    }
}
